home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / THINKC / 5 / SHOW_HEL / SHOW_HEL.C < prev    next >
C/C++ Source or Header  |  1992-03-05  |  33KB  |  1,170 lines

  1. #define USE_PICTS    1    /* 1 if PICTs are to be displayed, 0 for text only */
  2. #define ONE_RESOURCE 1    /* 1 for Get1Resource, 0 for GetResource, etc. */
  3. #define COMPRESSION    1    /* compressed TEXT/styl */
  4. #define STANDALONE    1
  5.  
  6. #define CHECKPOINTS 0
  7. #define ASSERTIONS    0
  8.  
  9. #if STANDALONE
  10.     #define        Show_help    main
  11. #else
  12.     void    main( void );
  13. #endif
  14.  
  15. #include "Show_help.h"
  16.  
  17. /*
  18.     Show_help by James W. Walker, June 1991
  19.     
  20.     version 1.6, updated March 1992
  21.     
  22.     This code is freely usable.  If you want to show your gratitude,
  23.     you could send me a free copy of whatever program you develop
  24.     with it.
  25.     
  26.     e-mail:
  27.         Internet            76367.2271@compuserve.com
  28.         CIS                    76367,2271
  29.         America Online        JWWalker
  30.     
  31.     This code displays scrolling text in a dialog box.  The text comes
  32.     from TEXT/styl resources, which can be created with ResEdit 2.1 or
  33.     with an accompanying HyperCard stack.
  34.     The text cannot be edited, but one can select text and copy it to
  35.     the clipboard using command-C, or save it as a TeachText file.
  36.     
  37.     Pictures can be included in the text using the same scheme as
  38.     TeachText: Each option-space character indicates where the top
  39.     edge of a picture should go, and pictures are centered horizontally.
  40.     Pictures come from consecutively-numbered PICT resources.  
  41.     
  42.     A popup menu can be used to jump to "bookmarks", which are indicated
  43.     by tab characters at ends of lines.
  44.     
  45.     Prototype:
  46.     
  47.     pascal void Show_help( short help_dlog_id, StringPtr help_text_name,
  48.         short base_pict_id, StringPtr default_filename,
  49.         StringPtr default_menuname );
  50.     
  51.     help_dlog_id  is the resource ID of the DLOG resource.  The dialog
  52.                   should have an OK button as item 1 and a userItem, to
  53.                   display the text, in item 2.
  54.     
  55.     help_text_name is the suffix of the resource name of the TEXT and
  56.                   styl resources.  For instance if help_text_name is
  57.                   "\pblah", and COMPRESSION is 1, then Show_help will
  58.                   look for '4CMP' resources named "TEXTblah" and "stylblah".
  59.                   If COMPRESSION is 0, Show_help looks for a 'TEXT'
  60.                   resource named "blah" and a 'styl' resource named "blah".
  61.     
  62.     base_pict_id is the resource ID of the first PICT resource.
  63.  
  64.     default_filename is the filename initially presented in the Save dialog
  65.                   when the user saves the help text as TeachText.
  66.     
  67.     default_menuname is the menu item used for a line that consists of
  68.                   a tab and nothing else.
  69.                   
  70.     
  71.     TO DO: error recovery, support for modeless use.
  72. */
  73.  
  74. #if __option(a4_globals)
  75.     #define        RESTORE_A4    asm { move.L (SP)+, A4 }
  76.     #if __option(pcrel_strings)        // single-segment code resource
  77.         #define        SETUP_A4    asm { move.L    A4, -(SP) }\
  78.                                 asm { LEA        main, A4 }
  79.     #else                            // multi-segment code resource
  80.         #include <SetUpA4.h>
  81.         #define        SETUP_A4    SetUpA4()
  82.     #endif
  83. #else
  84.     #define        SETUP_A4
  85.     #define        RESTORE_A4
  86. #endif
  87.  
  88.  
  89. #ifndef NIL
  90.     #define        NIL        0L
  91. #endif
  92.  
  93. #if ONE_RESOURCE
  94.     #define GetResource            Get1Resource
  95.     #define CountResources        Count1Resources
  96.     #define GetNamedResource    Get1NamedResource
  97. #endif ONE_RESOURCE
  98.  
  99. #if ASSERTIONS
  100.     #define        ASSERT(x,y)        if (!(x)) {DebugStr("\p" y);}
  101. #else
  102.     #define        ASSERT(x,y)
  103. #endif ASSERTIONS
  104.  
  105. #if CHECKPOINTS
  106.     #define CKPT(x)        DebugStr( "\p" x )
  107. #else
  108.     #define CKPT(x)
  109. #endif CHECKPOINTS
  110.  
  111. enum {
  112.     c_OK = 1,    /* OK button */
  113.     c_help,        /* userItem for our help display */
  114.     c_save,        /* Button to save as TeachText */
  115.     c_menu        /* userItem for popup menu */
  116. };
  117.  
  118. #define        SCROLLBAR_WIDTH    16
  119. #define        TEXT_INSET        4
  120.  
  121. typedef struct {
  122.     Rect        bounds;
  123.     PicHandle    pict;
  124. } pict_info;
  125.  
  126. typedef struct {
  127.     short        array_size;
  128.     short        high_waiting;
  129.     Rect        high_rect[];
  130. } high_info;
  131.  
  132. #define        INITIAL_HIGHLIGHTS        8
  133.  
  134. typedef struct { // piggyback some other info on the dialog record
  135.     DialogRecord    dialog;
  136.     ControlHandle    scrollbar;
  137. #if USE_PICTS
  138.     high_info        **high;
  139.     short            pict_count;    /* how many pictures */
  140.     pict_info        *pict_data;    /* pointer to an array */
  141.     Boolean            high_defer_flag;
  142. #endif
  143. }    help_record, *help_ptr;
  144.  
  145. #if COMPRESSION
  146. typedef struct {
  147.     ResType    srcType;
  148.     Handle    srcHandle;
  149.     ResType    dstType;
  150.     Handle    dstHandle;
  151. } ParmInfo;
  152.  
  153. /* We don't actually use the first parameter passed to the CNVT code,
  154.     but it is a pointer to a structure as below.
  155. */
  156. typedef struct {
  157.     ProcPtr        entryPoint;
  158.     short        resID;
  159.     short        parmCount;
  160.     Boolean        useDefault; /* 2 bytes? */
  161. } RoutineInfo;
  162.  
  163. typedef pascal OSErr (*CNVT_routine)(RoutineInfo *, ParmInfo *);
  164. #endif COMPRESSION
  165.  
  166.  
  167. /* private global variables */
  168. static    RgnHandle    save_clip;
  169. static    CursHandle    ibeam_cursor;
  170.  
  171. /* Prototypes of private routines
  172. */
  173. static void Push_highlight( high_info **hh, Rect *rect );
  174. static Boolean Pop_highlight( high_info **hh, Rect *rect );
  175. static pascal Boolean Help_filter( DialogPtr dialog,
  176.     EventRecord    *event, short *itemHit);
  177. static pascal void  Text_userItem_proc( WindowPtr the_window, short item_num );
  178. static pascal void  Menu_userItem_proc( WindowPtr the_window, short item_num );
  179. static pascal void Scroll_text( ControlHandle the_bar, int part_code );
  180. static pascal Auto_scroll( void );
  181. static void Handle_scroll( DialogPtr dialog,
  182.     short the_part, Point local_point );
  183. static void Adjust_text( DialogPtr dialog );
  184. static void Save_text( TEHandle the_text, short base_pict_id,
  185.     StringPtr default_filename );
  186. static void Topic_menu( DialogPtr dlog, MenuHandle help_popup );
  187. static MenuHandle Build_popup( TEHandle    the_text, StringPtr default_menuname );
  188. static short    Find_char(
  189.             Handle    data_h,        // handle to a block of characters
  190.             short    offset,        // initial offset within block
  191.             char    what );        // the character we're looking for
  192.  
  193. #if COMPRESSION
  194. static Handle Get_compressed_resource( ResType    the_type, StringPtr the_name );
  195. static void Release_compressed_resource( Handle rsrc_h );
  196. #else
  197. #define Get_compressed_resource            GetNamedResource
  198. #define Release_compressed_resource        ReleaseResource
  199. #endif
  200.  
  201. #if USE_PICTS
  202. static void Find_pictures( DialogPtr dlog, short first_pict_id );
  203. static void Draw_picts( WindowPtr the_window, Rect *update_rect );
  204. static pascal void High_hook( Rect *high_rect );
  205. static void High_hook_glue( void );
  206. static void Do_deferred_hilites( help_ptr  hptr, Rect *update_rect );
  207. #endif
  208.  
  209. /* ------------------------- Show_help --------------------------------- */
  210. pascal void Show_help( short help_dlog_id, StringPtr help_text_name,
  211.     short base_pict_id, StringPtr default_filename,
  212.     StringPtr default_menuname )
  213. {
  214.     register    DialogPtr    dptr;
  215.     register    TEHandle    the_text;
  216.     short        itype, ihit;
  217.     Handle        item_h;
  218.     Rect        help_item_box, box;
  219.     Handle            help_TEXT;
  220.     StScrpHandle    help_styl;
  221.     GrafPtr        save_port;
  222.     Rect        dest, view;
  223.     ControlHandle    the_bar;
  224.     short        max_scroll, nLines;
  225.     Point        place;
  226.     MenuHandle    help_popup;
  227.     CursHandle    watch_cursor;
  228. #if __option(a4_globals)
  229.     #if __option(pcrel_strings)    // single-segment code resource
  230.     asm {
  231.         LEA        main, A4
  232.     }
  233.     #else                        // multi-segment code resource
  234.         RememberA4();
  235.     #endif
  236. #endif
  237.  
  238.     watch_cursor = GetCursor( watchCursor );
  239.     SetCursor( *watch_cursor );
  240.     ibeam_cursor = GetCursor( iBeamCursor );
  241.     dptr = (DialogPtr) NewPtr( sizeof(help_record) );
  242.     dptr = GetNewDialog( help_dlog_id, (DialogPeek) dptr, (WindowPtr)-1L );
  243.     ASSERT( dptr != NIL, "Failed GetNewDialog" );
  244.     GetPort( &save_port );
  245.     SetPort( dptr );
  246.     
  247.     ((help_ptr) dptr)->high = (high_info **) NewHandle(
  248.                 sizeof(high_info) + INITIAL_HIGHLIGHTS * sizeof(Rect) );
  249.     ASSERT( ((help_ptr) dptr)->high != NIL, "Failed to get highlight record");
  250.     (**((help_ptr) dptr)->high).array_size = 2;
  251.     (**((help_ptr) dptr)->high).high_waiting = 0;
  252.     
  253.     help_TEXT = Get_compressed_resource( 'TEXT', help_text_name );
  254.     if (help_TEXT == NIL)
  255.     {
  256.         ASSERT(false, "Failed to find help TEXT resource" );
  257.         SysBeep(1);
  258.         goto getout;
  259.     }
  260.     help_styl = (StScrpHandle)
  261.         Get_compressed_resource( 'styl', help_text_name );
  262.     if (help_styl == NIL)
  263.     {
  264.         DisposHandle( help_TEXT );
  265.         ASSERT( false, "Failed to find styl resource" );
  266.         SysBeep(1);
  267.         goto getout;
  268.     }
  269.     HLock( help_TEXT );
  270.     
  271.     GetDItem( dptr, c_help, &itype, &item_h, &help_item_box );
  272.     SetDItem( dptr, c_help, itype, (Handle) Text_userItem_proc, &help_item_box );
  273.     view = help_item_box;
  274.     InsetRect( &view, 1, 1 );
  275.     view.right -= SCROLLBAR_WIDTH;
  276.     dest = view;
  277.     InsetRect( &dest, TEXT_INSET, 0 );
  278.     the_text = TEStylNew( &dest, &view );
  279.     ASSERT( the_text != NIL, "Failed TEStylNew." );
  280.     
  281.     TEStylInsert( *help_TEXT, GetHandleSize(help_TEXT),
  282.         help_styl, the_text );
  283.     TEActivate( the_text );
  284.     Release_compressed_resource( (Handle) help_styl );
  285.     Release_compressed_resource( help_TEXT );
  286.     nLines = (**the_text).nLines;
  287.     SetWRefCon( dptr, (long)the_text );
  288.     max_scroll = TEGetHeight( (long) nLines, 1L, the_text )
  289.         - (view.bottom - view.top);
  290.     
  291.     help_item_box.left = help_item_box.right - SCROLLBAR_WIDTH;
  292.     the_bar = NewControl( dptr, &help_item_box, "\p", true,
  293.         0, 0, max_scroll, scrollBarProc, NIL );
  294.     ASSERT( the_bar != NIL, "Failed NewControl for scroll bar." );
  295.     ((help_ptr) dptr)->scrollbar = the_bar;
  296.  
  297. #if USE_PICTS
  298.     Find_pictures( dptr, base_pict_id );
  299. #endif
  300.     help_popup = Build_popup( the_text, default_menuname );
  301.  
  302.     TEAutoView( TRUE, the_text );    /* Permit auto-scrolling */
  303.     CKPT( "Installing ClikLoop" );
  304.     (**the_text).clikLoop = (ProcPtr)Auto_scroll;
  305. #if USE_PICTS
  306.     (**the_text).highHook = (ProcPtr) High_hook_glue;
  307.     ((help_ptr)dptr)->high_defer_flag = false;
  308. #endif
  309.  
  310.     GetDItem( dptr, c_menu, &itype, &item_h, &box );
  311.     SetDItem( dptr, c_menu, itype, (Handle) Menu_userItem_proc, &box );
  312.     
  313.     save_clip = NewRgn();    /* Used in Draw_picts */
  314.     ShowWindow( dptr );
  315.     InitCursor();
  316.  
  317.     do {
  318.         ModalDialog( (ProcPtr) Help_filter, &ihit );
  319.         if (ihit == c_save)
  320.             Save_text( the_text, base_pict_id, default_filename );
  321.         else if (ihit == c_menu)
  322.             Topic_menu( dptr, help_popup );
  323.     } while (ihit != c_OK);
  324.     
  325.     
  326.     DisposeRgn( save_clip );
  327. #if USE_PICTS
  328.     DisposPtr( (Ptr) ((help_ptr)dptr)->pict_data );
  329. #endif
  330.     TEDispose( the_text );
  331. getout:
  332.     DisposDialog( dptr );
  333.     DisposeMenu( help_popup );
  334.     SetPort( save_port );
  335. }
  336.  
  337. /* --------------------------- Push_highlight -------------------------- */
  338. static void Push_highlight( high_info **hh, Rect *rect )
  339. {
  340.     if ( (**hh).high_waiting >= (**hh).array_size )
  341.     {
  342.         SetHandleSize( (Handle)hh, sizeof(high_info) +
  343.             (INITIAL_HIGHLIGHTS + (**hh).array_size) * sizeof(Rect) );
  344.         ASSERT( MemError() == noErr, "Can't expand highlight array" );
  345.         (**hh).array_size = ( GetHandleSize( (Handle)hh ) -
  346.                                     sizeof(high_info) ) / sizeof(Rect);
  347.     }
  348.     if ( (**hh).high_waiting < (**hh).array_size )
  349.     {
  350.         (**hh).high_rect[ (**hh).high_waiting ] = *rect;
  351.         (**hh).high_waiting++;
  352.     }
  353. }
  354.  
  355. /* --------------------------- Pop_highlight -------------------------- */
  356. static Boolean Pop_highlight( high_info **hh, Rect *rect )
  357. {
  358.     if ( (**hh).high_waiting > 0 )
  359.     {
  360.         (**hh).high_waiting--;
  361.         *rect = (**hh).high_rect[ (**hh).high_waiting ];
  362.         return true;
  363.     }
  364.     else
  365.         return false;
  366. }
  367.  
  368. /* --------------------------- Find_char -------------------------- */
  369. /*
  370.     Find a character within a handle.  In a previous version I did this
  371.     with Munger().
  372.     
  373.     returns: the offset of the character, or -1 if not found.
  374. */
  375. static short    Find_char(
  376.             Handle    data_h,        // handle to a block of characters
  377.             short    offset,        // initial offset within block
  378.             char    what )        // the character we're looking for
  379. {
  380.     Ptr        text;
  381.     short    text_size, scan;
  382.     
  383.     text_size = (short) GetHandleSize( data_h );
  384.     text = *data_h;
  385.     for (scan = offset; (text[scan] != what) && (scan < text_size); ++scan)
  386.         ;
  387.     if (scan == text_size) // not found
  388.         scan = -1;
  389.     return scan;
  390. }
  391.  
  392. /* --------------------------- Build_popup ------------------------- */
  393. /*
  394.     Build a popup menu of the sections of the help text.  We scan for
  395.     tab characters.  The text between the tab character and the preceding
  396.     line break will be a menu item, unless it is the null string; then we
  397.     use the default menu name that was passed to Show_help.
  398. */
  399. static MenuHandle Build_popup( TEHandle    the_text, StringPtr default_menuname )
  400. {
  401.     MenuHandle    popup;
  402.     short        menu_id;
  403.     SignedByte    text_state;
  404.     Handle        text_h;    /* handle to just the text */
  405.     Str255        menu_data;
  406.     char        *text;    /* pointer to the help text */
  407.     register short        scan, line_start;
  408.     short        text_size, title_length;
  409.     
  410.     /* Find an unused menu ID */
  411.     menu_id = 1300; /* no particular reason */
  412.     while (GetMHandle(menu_id))
  413.         ++menu_id;
  414.     
  415.     popup = NewMenu( menu_id, "\p" );
  416.  
  417.     text_h = (**the_text).hText;
  418.     text_state = HGetState( text_h );
  419.     HLock( text_h );
  420.     text = *text_h;
  421.     text_size = (short) GetHandleSize( text_h );
  422.     line_start = 0;
  423.     for (scan = 0; scan < text_size; scan++ )
  424.     {
  425.         if (text[scan] == '\r')
  426.         {
  427.             line_start = scan + 1;
  428.         }
  429.         else if (text[scan] == '\t')
  430.         {
  431.             title_length = scan - line_start;
  432.             if (title_length == 0)
  433.                 BlockMove( default_menuname, menu_data, 256 );
  434.             else
  435.             {
  436.                 menu_data[0] = title_length; // note: <= 255
  437.                 BlockMove( &text[line_start], &menu_data[1], menu_data[0] );
  438.             }
  439.             /*
  440.                 AppendMenu recognizes meta-characters like slash,
  441.                 which is probably not what we want in this case.  So
  442.                 we use SetItem, which does not use meta-characters.
  443.             */
  444.             AppendMenu( popup, "\p " );
  445.             SetItem( popup, CountMItems(popup), menu_data );
  446.         }
  447.     }
  448.     
  449.     HSetState( text_h, text_state );
  450.     return popup;
  451. }
  452.  
  453. /* ------------------------- Topic_menu ------------------------ */
  454. /*
  455.     This routine is called when the menu title is clicked.
  456.     It pops up the menu and scrolls to the indicated tab character.
  457. */
  458. static void Topic_menu( DialogPtr dptr, MenuHandle menu )
  459. {
  460.     short            menu_id;
  461.     Handle            item_h;
  462.     short            itype;
  463.     Rect            box;
  464.     Point            where;
  465.     long            menu_return;
  466.     short            menu_choice;
  467.     ControlHandle    bar;
  468.     register short        i;
  469.     register TEHandle    the_text;
  470.     Handle            text_h;
  471.     register short        offset;
  472.     TextStyle        what_style;
  473.     short            line_height, font_ascent;
  474.     
  475.     if (menu == NIL) return;
  476.     InsertMenu( menu, -1 );
  477.     GetDItem( dptr, c_menu, &itype, &item_h, &box );
  478.     where.h = box.left;
  479.     where.v = box.bottom;
  480.     LocalToGlobal( &where );
  481.     HiliteMode &= ~(1 << hiliteBit);
  482.     InvertRect( &box );
  483.     menu_return = PopUpMenuSelect( menu, where.v, where.h, 0 );
  484.     HiliteMode &= ~(1 << hiliteBit);
  485.     InvertRect( &box );
  486.     if (HiWord(menu_return))    /* Something selected */
  487.     {
  488.         menu_choice = LoWord( menu_return );
  489.         bar = ((help_ptr)dptr)->scrollbar;
  490.         the_text = (TEHandle) GetWRefCon( dptr );
  491.         text_h = (**the_text).hText;
  492.         
  493.         /* Find tab character number menu_choice */
  494.         offset = -1L;
  495.         for (i = 1; i <= menu_choice; ++i)
  496.         {
  497.             ++offset; /* so we don't find the same thing twice */
  498.             offset = Find_char( text_h, offset, '\t' );
  499.         }
  500.  
  501.         where = TEGetPoint( (short)offset, the_text );
  502.         TEGetStyle( (short)offset, &what_style, &line_height,
  503.             &font_ascent, the_text );
  504.         where.v -= line_height;    /* align to TOP of tab */
  505.         /*
  506.             Now where.v is in local coordinates.
  507.         */
  508.         where.v -= (**the_text).destRect.top;
  509.         SetCtlValue( bar,  where.v );
  510.         
  511.         Adjust_text( dptr );
  512.     }
  513.     menu_id = (**menu).menuID;
  514.     DeleteMenu( menu_id );
  515. }
  516.  
  517. /* ------------------------- Save_text ------------------------ */
  518. /*
  519.     This is called when the user clicks on the "Save as TeachText"
  520.     button.
  521. */
  522. static void Save_text( TEHandle the_text, short base_pict_id,
  523.     StringPtr default_filename )
  524. {
  525.     Point        where;
  526.     SFReply        reply;
  527.     OSErr        err;
  528.     short        data_refnum, res_refnum, old_resfile;
  529.     Handle        text_data;
  530.     SignedByte    state;
  531.     long        count;
  532. #if USE_PICTS
  533.     register short        num_picts, pict_id;
  534.     Handle        old_pict, new_pict;
  535. #endif
  536.     
  537.     where.h = where.v = 100;
  538.     SFPutFile( where, "\pName of TeachText file:",
  539.         default_filename, NIL, &reply );
  540.     if (reply.good)
  541.     {
  542.         old_resfile = CurResFile();
  543. #if USE_PICTS
  544.         num_picts = CountResources( 'PICT' );
  545. #endif
  546.         
  547.         (void) FSDelete( reply.fName, reply.vRefNum );
  548.         (void) Create( reply.fName, reply.vRefNum, 'ttxt', 'ttro' );
  549.         (void) FSOpen( reply.fName, reply.vRefNum, &data_refnum );
  550.         text_data = (**the_text).hText;
  551.         state = HGetState(text_data );
  552.         HLock( text_data );
  553.         count = GetHandleSize( text_data );
  554.         (void) FSWrite( data_refnum, &count, *text_data );
  555.         (void) FSClose( data_refnum );
  556.         HSetState( text_data, state );
  557.  
  558. #if USE_PICTS
  559.         if (num_picts > 0)
  560.         {
  561.             (void) SetVol( NIL, reply.vRefNum );
  562.             CreateResFile( reply.fName );
  563.             ASSERT( ResError() == noErr, "\pCreateResFile error" );
  564.             res_refnum = OpenResFile( reply.fName );
  565.             ASSERT( ResError() == noErr, "\pOpenResFile error" );
  566.             for (pict_id = base_pict_id;
  567.                 pict_id < base_pict_id + num_picts; ++pict_id )
  568.             {
  569.                 UseResFile( old_resfile );
  570.                 old_pict = GetResource( 'PICT', pict_id );
  571.                 if (old_pict == NIL)
  572.                     break;
  573.                 new_pict = old_pict;
  574.                 (void) HandToHand( &new_pict );
  575.                 UseResFile( res_refnum );
  576.                 AddResource( new_pict, 'PICT',
  577.                     pict_id - base_pict_id + 1000, "\p" );
  578.                 ASSERT( ResError() == noErr, "\pAddResource error" );
  579.             }
  580.             CloseResFile( res_refnum );
  581.             (void) FlushVol( NIL, reply.vRefNum );
  582.         }
  583. #endif
  584.     }
  585. }
  586.  
  587. #if USE_PICTS
  588. /* ------------------------- High_hook_glue ------------------------ */
  589. static void High_hook_glue( void )
  590. {
  591.     asm {
  592.         move.L    (SP)+, A0    ; get address of rectangle
  593.         movem.L    A2-A5/D3-D7, -(SP)    ; save registers
  594.         move.L    A0, -(SP)
  595.     }
  596.     CKPT( "High_hook_glue" );
  597.     asm {
  598.         JSR        High_hook
  599.         movem.L    (SP)+, A2-A5/D3-D7    ; restore registers
  600.         RTS
  601.     }
  602. }
  603.  
  604. /* ------------------------- High_hook -------------------------- */
  605. /*
  606.     This deferred highlighting scheme is used to ensure that highlighting
  607.     will be done after any pictures have been drawn, not before.  To do
  608.     otherwise can cause pictures to be incorrectly highlighted during
  609.     auto-scrolling.  This effect can be seen in TeachText.
  610. */
  611. static pascal void High_hook( Rect *the_rect )
  612. {
  613.     register help_ptr    front;
  614.     
  615.     CKPT( "High_hook");
  616.     front = (help_ptr) FrontWindow();
  617.     if ( (front != NIL) &&
  618.         (GetPtrSize((Ptr)front) == sizeof(help_record)) &&
  619.         !EmptyRect( the_rect ) )
  620.     {
  621.         if (!front->high_defer_flag)
  622.         {
  623.             HiliteMode &= ~(1 << hiliteBit);
  624.             InvertRect( the_rect );
  625.         }
  626.         else
  627.         {
  628.             Push_highlight( front->high, the_rect );
  629.         }
  630.     }
  631. }
  632. #endif /* USE_PICTS */
  633.  
  634. /* ------------------------- Auto_scroll ----------------------------- */
  635. /*
  636.     This is a ClikLoop routine, called repeatedly by TEClick when the
  637.     mouse is down.
  638. */
  639. static pascal Auto_scroll()
  640. {
  641.     register    WindowPtr    the_display;
  642.     register    ControlHandle    the_bar;
  643.     Point                    mouse_point;
  644.     Rect                    view_rect;
  645.     register     TEHandle    the_text;
  646.     
  647.     asm {
  648.         movem.l        a1-a5/d1-d7, -(SP)
  649.     }
  650.     CKPT( "Auto_scroll");
  651.     the_display = FrontWindow();
  652.     if ( (the_display != NIL) &&
  653.         (GetPtrSize((Ptr)the_display) == sizeof(help_record)) )
  654.     {
  655.         the_text = (TEHandle) GetWRefCon( the_display );
  656.         the_bar = ((help_ptr) the_display)->scrollbar;
  657.         
  658.         GetMouse( &mouse_point );
  659.         view_rect = (**the_text).viewRect;
  660.         if (mouse_point.v < view_rect.top)
  661.             Scroll_text( the_bar, inUpButton );
  662.         else if (mouse_point.v > view_rect.bottom)
  663.             Scroll_text( the_bar, inDownButton );
  664.     }
  665.     asm {
  666.         movem.L        (SP)+, a1-a5/d1-d7
  667.         moveQ        #1, D0
  668.     }
  669. }
  670.  
  671. #if USE_PICTS
  672. /* ------------------------- Draw_picts --------------------------------- */
  673. /*
  674.     Called by Adjust_text and Text_userItem_proc to draw pictures.
  675. */
  676. static void Draw_picts( WindowPtr the_window, Rect *update_rect )
  677. {
  678.     register TEHandle    the_text;
  679.     register short        pict_count, pict_index;
  680.     PicHandle    the_pict;
  681.     short         v_offset;
  682.     Rect        pict_loc, dummy;
  683.     
  684.     CKPT( "Draw_picts");
  685.     the_text = (TEHandle) GetWRefCon( the_window );
  686.     v_offset = (**the_text).destRect.top - (**the_text).viewRect.top
  687.         - TEXT_INSET;
  688.     pict_count = ((help_ptr) the_window)->pict_count;
  689.     for (pict_index = 0; pict_index < pict_count; pict_index++)
  690.     {
  691.         pict_loc = ((help_ptr) the_window)->pict_data[pict_index].bounds;
  692.         OffsetRect( &pict_loc, 0, v_offset );
  693.         if (!SectRect( &pict_loc, update_rect, &dummy ))
  694.             continue;
  695.         the_pict = ((help_ptr) the_window)->pict_data[pict_index].pict;
  696.         LoadResource( (Handle) the_pict );
  697.         GetClip( save_clip );
  698.         ClipRect( update_rect );
  699.         DrawPicture( the_pict, &pict_loc );
  700.         SetClip( save_clip );
  701.     }
  702. }
  703.  
  704. #define        OPTION_SPACE_CHAR    0xCA
  705.  
  706. /* ---------------------- Find_pictures ---------------------------- */
  707. static void Find_pictures( DialogPtr dlog, short first_pict_id )
  708. {
  709.     register TEHandle    the_text;
  710.     Handle        text_h;
  711.     SignedByte    text_state;
  712.     register short        offset;
  713.     short        num_picts;
  714.     register short        which_pict;
  715.     pict_info    *pict;
  716.     Point        place;
  717.     short        line_height, font_ascent;
  718.     TextStyle    what_style;
  719.     
  720.     CKPT( "Find_pictures");
  721.     the_text = (TEHandle) GetWRefCon( dlog );
  722.     text_h = (**the_text).hText;
  723.     text_state = HGetState( text_h );
  724.     HLock( text_h );
  725.     
  726.     /* Count option-space characters in the text. */
  727.     offset = 0;
  728.     num_picts = 0;
  729.     offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
  730.     while ( offset >= 0 )
  731.     {
  732.         num_picts++;
  733.         offset++;
  734.         offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
  735.     }
  736.     
  737.     /* Allocate storage for an array of picture bounds. */
  738.     pict = (pict_info *) NewPtr( sizeof(pict_info) * num_picts );
  739.     ((help_ptr)dlog)->pict_data = pict;
  740.     
  741.     /*
  742.         Initialize the picture info.  For each picture we record the
  743.         picture handle and its rectangle, in unscrolled window
  744.         coordinates.
  745.     */
  746.     offset = 0;
  747.     for (which_pict = 0; which_pict < num_picts; which_pict++)
  748.     {
  749.         pict[which_pict].pict = (PicHandle) GetResource( 'PICT',
  750.             first_pict_id + which_pict );
  751.         if ( pict[which_pict].pict == NIL )
  752.             break;
  753.         offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
  754.         place = TEGetPoint( offset, the_text );
  755.         TEGetStyle( offset, &what_style, &line_height,
  756.             &font_ascent, the_text );
  757.         place.v -= line_height;    /* align picture with TOP of option-space */
  758.         offset++;
  759.         pict[which_pict].bounds = (**pict[which_pict].pict).picFrame;
  760.         OffsetRect( &pict[which_pict].bounds,
  761.             ( ((**the_text).destRect.right + (**the_text).destRect.left) -
  762.             (pict[which_pict].bounds.right + pict[which_pict].bounds.left)
  763.             ) / 2,
  764.             - pict[which_pict].bounds.top + place.v );
  765.     }
  766.     ((help_ptr)dlog)->pict_count = which_pict;
  767.     
  768. getout:
  769.     HSetState( text_h, text_state );
  770. }
  771. #endif /* USE_PICTS */
  772.  
  773. /* ---------------------- Scroll_text ---------------------------- */
  774. /*
  775.     This is used as a TrackControl actionProc for scrolling, and also
  776.     called by Auto_scroll for automatic scrolling.
  777. */
  778. static pascal void Scroll_text( ControlHandle the_bar, int part_code )
  779. {
  780.     register TEHandle    the_text;
  781.     register short        delta;
  782.     register WindowPtr    the_display;
  783.     short                old_value;
  784.     short                offset, line;
  785.     Point                place;
  786.     Rect                view;
  787.     TextStyle            style;
  788.     short                line_height, font_ascent;
  789.     
  790.     CKPT( "Scroll_text");
  791.     SETUP_A4;                // for access to static global save_clip
  792.     if (part_code != 0)
  793.     {
  794.         the_display = (**the_bar).contrlOwner;
  795.         the_text = (TEHandle) GetWRefCon( the_display );
  796.         view = (**the_text).viewRect;
  797.         place.h = view.left + TEXT_INSET;
  798.         
  799.         switch (part_code)
  800.         {
  801.             case inUpButton:
  802.                 place.v = view.top - 4;
  803.                 offset = TEGetOffset( place, the_text );
  804.                 place = TEGetPoint( offset, the_text );
  805.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  806.                     the_text );
  807.                 delta = place.v - line_height - view.top;
  808.                 break;
  809.             case inDownButton:
  810.                 place.v = view.bottom + 2;
  811.                 offset = TEGetOffset( place, the_text );
  812.                 place = TEGetPoint( offset, the_text );
  813.                 /* Now place.v is at the baseline of the border line. */
  814.                 delta = place.v - view.bottom;
  815.                 break;
  816.             case inPageUp:
  817.                 /*
  818.                     I want top border line to remain visible, and
  819.                     the top of a line should end up at view.top.
  820.                 */
  821.                 place.v = view.top + 2;
  822.                 offset = TEGetOffset( place, the_text );
  823.                 place = TEGetPoint( offset, the_text );
  824.                 /* place.v is at the baseline of the top border line. */
  825.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  826.                     the_text );
  827.                 place.v += line_height - font_ascent;
  828.                 place.v -= view.bottom - view.top;
  829.                 offset = TEGetOffset( place, the_text );
  830.                 place = TEGetPoint( offset, the_text );
  831.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  832.                     the_text );
  833.                 delta = place.v - view.top;
  834.                 if (offset == 0)
  835.                     delta -= line_height;
  836.                 break;
  837.             case inPageDown:
  838.                 /*
  839.                     I want bottom border line to remain visible, and
  840.                     the bottom of a line should end up at view.bottom.
  841.                 */
  842.                 place.v = view.bottom - 2;
  843.                 offset = TEGetOffset( place, the_text );
  844.                 place = TEGetPoint( offset, the_text );
  845.                 /* place.v is at the baseline of the bottom border line. */
  846.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  847.                     the_text );
  848.                 place.v -= font_ascent; /* Top edge of bottom border line */
  849.                 place.v += view.bottom - view.top;
  850.                 /* We're looking at the bottom border of the next page. */
  851.                 offset = TEGetOffset( place, the_text );
  852.                 place = TEGetPoint( offset, the_text );
  853.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  854.                     the_text );
  855.                 delta =  place.v - line_height - view.bottom;
  856.                 if (offset == (**the_text).teLength)
  857.                     delta += line_height;
  858.                 break;
  859.         }
  860.         old_value = GetCtlValue( the_bar );
  861.         if ( ((delta < 0) && (old_value > 0)) ||
  862.             ((delta > 0) && (old_value < GetCtlMax(the_bar))) )
  863.         {
  864.             /*
  865.                 When this routine is called, TextEdit may have set the
  866.                 clipping region to the view rectangle, so we reset it
  867.                 here to make sure the scroll bar gets drawn.
  868.             */
  869.             GetClip( save_clip );
  870.             ClipRect( &the_display->portRect );
  871.             SetCtlValue( the_bar, old_value + delta );
  872.             SetClip( save_clip );
  873.         }
  874.         Adjust_text( the_display );
  875.     }
  876.     RESTORE_A4;
  877. }
  878.  
  879. /* ---------------------- Adjust_text ---------------------------- */
  880. /*
  881.     Called by Handle_scroll and Scroll_text to scroll the text and
  882.     pictures into sync with the scroll bar's control value.
  883. */
  884. static void Adjust_text( DialogPtr    dialog )
  885. {
  886.     register    TEHandle    the_text;
  887.     register    short    scroll_down;
  888.     short            old_scroll;
  889.     Rect            update_rect;
  890.     ControlHandle    the_bar;
  891.     
  892.     CKPT( "Adjust_text");
  893.     the_text = (TEHandle) GetWRefCon( dialog );
  894.     the_bar = ((help_ptr) dialog)->scrollbar;
  895.     old_scroll = (**the_text).viewRect.top - (**the_text).destRect.top;
  896.     scroll_down = old_scroll - GetCtlValue( the_bar );
  897.     if (scroll_down == 0)
  898.         return;
  899. #if USE_PICTS
  900.     ((help_ptr) dialog)->high_defer_flag = true;
  901.     //((help_ptr) dialog)->high_waiting = 0;
  902. #endif
  903.     TEScroll( 0, scroll_down, the_text );
  904. #if USE_PICTS
  905.     update_rect = (**the_text).viewRect;
  906.     if (scroll_down > 0)
  907.     {
  908.         if (scroll_down < (update_rect.bottom - update_rect.top))
  909.             update_rect.bottom = update_rect.top + scroll_down;
  910.     }
  911.     else
  912.         if (- scroll_down < (update_rect.bottom - update_rect.top))
  913.             update_rect.top = update_rect.bottom + scroll_down;
  914.     Draw_picts( dialog, &update_rect );
  915.     Do_deferred_hilites( (help_ptr) dialog, &update_rect );
  916. #endif
  917. }
  918.  
  919. #if USE_PICTS
  920. /* ---------------------- Do_deferred_hilites ---------------------- */
  921. static void Do_deferred_hilites( help_ptr  hptr, Rect *update_rect )
  922. {
  923.     Rect    hilite;
  924.     
  925.     while (Pop_highlight( hptr->high, &hilite ))
  926.     {
  927.         if (SectRect( &hilite, update_rect, &hilite ))
  928.         {
  929.             HiliteMode &= ~(1 << hiliteBit);
  930.             InvertRect( &hilite );
  931.         }
  932.     }
  933.     hptr->high_defer_flag = false;
  934. }
  935. #endif
  936.  
  937. /* ---------------------- Handle_scroll ---------------------------- */
  938. /*
  939.     Called by Help_filter to handle mouseDown events in the scroll bar.
  940. */
  941. static void Handle_scroll( DialogPtr dialog, short the_part, Point where )
  942. {
  943.     register    ControlHandle the_bar;
  944.     
  945.     CKPT( "Handle_scroll"); SetPort( dialog );
  946.     the_bar = ((help_ptr) dialog)->scrollbar;
  947.     if (the_part == inThumb)
  948.     {
  949.         (void) TrackControl( the_bar, where, NIL );
  950.         Adjust_text( dialog );
  951.     }
  952.     else
  953.         (void) TrackControl( the_bar, where, (ProcPtr)Scroll_text );
  954.     
  955. }
  956.  
  957. /* ---------------------- Help_filter ------------------------- */
  958. /*
  959.     This is the dialog event filter for our help window.
  960. */
  961. #define        RETURN_CHAR        0x0D
  962. #define        TILDE_CHAR        0x7E
  963. #define        ENTER_CHAR        0x03
  964. #define        ESCAPE_CHAR        0x1B
  965.  
  966. static pascal Boolean Help_filter( DialogPtr dialog,
  967.     EventRecord    *event, short *itemHit)
  968. {
  969.     Point    local_point;
  970.     short    the_part;
  971.     ControlHandle    the_control;
  972.     short    charcode;
  973.     register TEHandle    the_text;
  974.     Rect    item_box;
  975.     short    cursor;
  976.     
  977.     SETUP_A4;    // for access to static global ibeam_cursor
  978.     the_text = (TEHandle) GetWRefCon( dialog );
  979.     GetMouse( &local_point );
  980.     if (PtInRect( local_point, &(**the_text).viewRect ))
  981.         SetCursor( *ibeam_cursor );
  982.     else
  983.         InitCursor();
  984.     TEIdle( the_text );
  985.     switch (event->what) {
  986.         case nullEvent:
  987.             break;
  988.         case mouseDown:
  989.             CKPT( "Help_filter mousedown");
  990.             local_point = event->where;
  991.             GlobalToLocal( &local_point );
  992.             the_part = FindControl( local_point, dialog, &the_control );
  993.             if (the_part && ((**the_control).contrlMax > 1) )
  994.             {
  995.                 Handle_scroll( dialog, the_part, local_point );    
  996.                 *itemHit = 2;
  997.                 break;
  998.             }
  999.             if (PtInRect( local_point, &(**the_text).viewRect ))
  1000.             {
  1001.                 if (event->modifiers & shiftKey)
  1002.                     TEClick( local_point, true, the_text );
  1003.                 else
  1004.                     TEClick( local_point, false, the_text );
  1005.             }
  1006.             break;
  1007.         case keyDown :    
  1008.         case autoKey :
  1009.             charcode = event->message & charCodeMask;
  1010.             /*
  1011.                 There's no Cancel button, so we treat the OK button
  1012.                 the same as a Cancel button.
  1013.             */
  1014.             if ( (charcode == RETURN_CHAR) || (charcode == ENTER_CHAR) ||
  1015.                 (charcode == TILDE_CHAR) || (charcode == ESCAPE_CHAR) ||
  1016.                 ((charcode == '.') && (event->modifiers & cmdKey)) )
  1017.             {
  1018.                 *itemHit = c_OK;    /* OK */
  1019.                 Flash_button( dialog, *itemHit );
  1020.                 RESTORE_A4;
  1021.                 return( TRUE );
  1022.             }
  1023.             if ( (charcode == 'c') && (event->modifiers & cmdKey) )
  1024.             {
  1025.                 (void) ZeroScrap();
  1026.                 TECopy( the_text );
  1027.                 SystemEdit(3);
  1028.                 local_point = (**the_text).selPoint;
  1029.                 *(long *)&local_point = PinRect( &(**the_text).viewRect,
  1030.                     local_point );
  1031.                 cursor = TEGetOffset( local_point, the_text );
  1032.                 TESetSelect( (long)cursor, (long)cursor, the_text );
  1033.                 event->what = nullEvent;
  1034.             }
  1035.             break;
  1036.     } /* end switch */
  1037.     
  1038.     /* tell the Dialog Manager that the event has NOT been handled and that it should
  1039.     ** take further action on this event.
  1040.     */
  1041.     RESTORE_A4;
  1042.     return false;
  1043. }
  1044.  
  1045.  
  1046. /* ---------------------- Text_userItem_proc ------------------------- */
  1047. static pascal void  Text_userItem_proc( WindowPtr the_window, short item_num )
  1048. {
  1049.     Handle        item_h;
  1050.     Rect        item_box;
  1051.     short        item_type;
  1052.     TEHandle    the_text;
  1053.     
  1054.     SETUP_A4;    // for access to static global save_clip
  1055.     
  1056.     CKPT( "Text_userItem_proc");
  1057.     the_text = (TEHandle) GetWRefCon( the_window );
  1058.     item_box = (**the_text).viewRect;
  1059. #if USE_PICTS
  1060.     ((help_ptr) the_window)->high_defer_flag = true;
  1061. #endif
  1062.     TEUpdate( &item_box, the_text );
  1063.     
  1064. #if USE_PICTS
  1065.     Draw_picts( the_window, &item_box );
  1066.     Do_deferred_hilites( (help_ptr) the_window, &item_box );
  1067. #endif
  1068.  
  1069.     /*
  1070.         Get the item's rectangle, and frame it.
  1071.     */
  1072.     GetDItem( the_window, item_num, &item_type, &item_h, &item_box );
  1073.     FrameRect( &item_box );
  1074.  
  1075.     RESTORE_A4;
  1076. }
  1077.  
  1078. /* ---------------------- Menu_userItem_proc ------------------------- */
  1079. static pascal void  Menu_userItem_proc( WindowPtr the_window, short item_num )
  1080. {
  1081.     Handle        item_h;
  1082.     Rect        item_box;
  1083.     short        item_type;
  1084.     
  1085.     asm {
  1086.         MOVEM.L    a1-a5/d0-d7, -(SP)
  1087.     }
  1088.     
  1089.     CKPT( "Menu_UserItem_proc");
  1090.     
  1091.     /*
  1092.         Get the item's rectangle, and frame it.
  1093.     */
  1094.     GetDItem( the_window, item_num, &item_type, &item_h, &item_box );
  1095.     InsetRect(&item_box, -1, -1);
  1096.     FrameRect( &item_box );
  1097.     
  1098.     /* Draw the drop-shadow */
  1099.     MoveTo( item_box.left + 3, item_box.bottom );
  1100.     LineTo( item_box.right, item_box.bottom );
  1101.     LineTo( item_box.right, item_box.top + 3 );        
  1102.  
  1103.     asm {
  1104.         movem.l    (SP)+, a1-a5/d0-d7        ; restore registers
  1105.     }
  1106. }
  1107.  
  1108. /* ---------------------- Flash_button ----------------------------- */
  1109. pascal void Flash_button( DialogPtr the_dialog, short item_number )
  1110. {
  1111.     ControlHandle    item_h;
  1112.     long    time;
  1113.     short    itype;
  1114.     Rect    box;
  1115.     
  1116.     GetDItem( the_dialog, item_number, &itype, (Handle *)&item_h, &box );
  1117.     HiliteControl( item_h, inButton );
  1118.     Delay( 9L, &time );
  1119.     HiliteControl( item_h, 0 );
  1120. }
  1121.  
  1122. #if COMPRESSION
  1123. /* ---------------------- Get_compressed_resource ----------------------- */
  1124. static Handle Get_compressed_resource( ResType the_type, StringPtr the_name )
  1125. {
  1126.     register Handle        CNVT_h;
  1127.     ParmInfo        info;
  1128.     Str255            name;
  1129.     CNVT_routine    Converter;
  1130.     
  1131.     name[0] = the_name[0] + 4;
  1132.     BlockMove( &the_type, &name[1], 4 );
  1133.     BlockMove( &the_name[1], &name[5], the_name[0] );
  1134.     info.srcHandle = GetNamedResource( '4CMP', name );
  1135.     CNVT_h = GetNamedResource( 'CNVT', "\p4CMPUncompress 4CMP" );
  1136.     if ( (info.srcHandle == NIL) ||    /* maybe there's an uncompressed one */
  1137.         (CNVT_h == NIL) )
  1138.     {
  1139.         info.dstHandle = GetNamedResource( the_type, the_name );
  1140.     }
  1141.     else    /* found a compressed resource */
  1142.     {
  1143.         info.srcType = '4CMP';
  1144.  
  1145.         HLock(CNVT_h);
  1146.         Converter = (CNVT_routine) StripAddress( *CNVT_h );
  1147.         CKPT("\pAbout to call the CNVT");
  1148.         (void) Converter( NIL, &info );
  1149.         CKPT("\pAfter the CNVT");
  1150.         HUnlock(CNVT_h);
  1151.         
  1152.         ReleaseResource( info.srcHandle );
  1153.     }
  1154.     return    info.dstHandle;
  1155. }
  1156.  
  1157. #define        mem_resource    0x20
  1158.  
  1159. /* ------------------------- Release_compressed_resource ------------- */
  1160. /*
  1161.     If it's a resource handle, release it, otherwise dispose of it.
  1162. */
  1163. static void Release_compressed_resource( Handle rsrc_h )
  1164. {
  1165.     if ( HGetState( rsrc_h ) & mem_resource )
  1166.         ReleaseResource( rsrc_h );
  1167.     else
  1168.         DisposHandle( rsrc_h );
  1169. }
  1170. #endif COMPRESSION